package cn.sc.summer.gateway.util;

import cn.hutool.extra.spring.SpringUtil;
import cn.sc.summer.constant.project.ProjectConstant;
import cn.sc.summer.constant.thread.ThreadConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 类名：动态线程池工具类，按照服务器资源cpu核数定义
 *
 * @author a-xin
 * @date 15:17
 */
@Slf4j
@Configuration
public class ThreadUtil {

    /**
     * 自定义线程池<br/>
     * corePoolSize - 即使空闲时仍保留在池中的线程数，除非设置allowCoreThreadTimeOut<br/>
     * maximumPoolSize - 池中允许的最大线程数<br/>
     * keepAliveTime - 当线程数大于内核时，这是多余的空闲线程在终止前等待新任务的最大时间<br/>
     * unit - keepAliveTime参数的时间单位<br/>
     * workQueue - 用于在执行任务之前使用的队列，这个队列将仅保存execute方法提交的Runnable任务<br/>
     * threadFactory - 执行程序创建新线程时使用的工厂<br/>
     * handler - 执行被阻止时使用的处理程序，因为达到线程限制和队列容量<br/>
     * <p>
     * RejectedExecutionHandler rejected = null;<br/>
     * - rejected = new ThreadPoolExecutor.AbortPolicy();//默认，队列满了丢任务，抛出异常<br/>
     * - rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务，不抛出异常【如果允许任务丢失这是最好的】<br/>
     * - rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删，之后再尝试加入队列<br/>
     * - rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败，那么主线程会自己去执行该任务，回退
     */
    @Bean(value = ThreadConstant.THREAD_POOL_BEAN)
    public ThreadPoolExecutor THREAD_POOL() {
        log.info("==> Start initializing the thread pool configuration：serviceName:{}, corePoolSize:{}, maximumPoolSize:{}, keepAlive:{} second",
                ProjectConstant.SERVER_NAME,
                ThreadConstant.CORE_POOL_SIZE,
                ThreadConstant.MAXIMUM_POOL_SIZE,
                ThreadConstant.KEEP_ALIVE);
        return new ThreadPoolExecutor(
                ThreadConstant.CORE_POOL_SIZE,
                ThreadConstant.MAXIMUM_POOL_SIZE,
                ThreadConstant.KEEP_ALIVE,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(ThreadConstant.WORK_QUEUE),
                r -> new Thread(r, ProjectConstant.SERVER_NAME + "-" + r.hashCode()),
                new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    /**
     * 线程池关闭操作
     */
    @PreDestroy
    private void destroy() {
        ThreadPoolExecutor THREAD_POOL_EXECUTOR = SpringUtil.getBean(ThreadConstant.THREAD_POOL_BEAN, ThreadPoolExecutor.class);
        if (THREAD_POOL_EXECUTOR != null && !THREAD_POOL_EXECUTOR.isShutdown()) {
            THREAD_POOL_EXECUTOR.shutdown();
            try {
                if (!THREAD_POOL_EXECUTOR.awaitTermination(10, TimeUnit.SECONDS)) {
                    THREAD_POOL_EXECUTOR.shutdownNow();
                    if (!THREAD_POOL_EXECUTOR.awaitTermination(10, TimeUnit.SECONDS)) {
                        log.info("Pool did not terminate!");
                    }
                }
            } catch (InterruptedException ie) {
                THREAD_POOL_EXECUTOR.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

}
